import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttercommon/src/strings.dart';

import '../customizable.dart';
import '../http/file_upload.dart';
import '../http/http_configs.dart';
import '../http/http_wrapper.dart';
import '../pickers/photo_picker.dart';
import '../widgets/http_request_loading.dart';
import 'action_buttons.dart';

typedef Future<ApiResult> UploadPhotoFunction(String? path);

typedef void PhotoUploadStatusChangedCallback(
  FileUploadBean? photo,
  UploadFileStatus status,
);

///Features:
///1. 支持单张图片和多张图片
///2. 单张图片时可以指定宽高
///3. 多张图片时可以指定单行最多显示的图片数, 最大高度.
///4. 支持图片上传.
///5. 支持删除图片.
///6. 支持默认显示的图片.
///7. 无图片时显示提示上传的按钮,文本或图标
class PhotoWidget extends StatefulWidget {
  final FileUploadBean? photo;
  final double? width;
  final double? height;
  final BoxFit fit;
  final String placeholder;
  final bool editable;
  final UploadPhotoFunction? uploadPhotoFunction;
  final PreviewPhotoFunction? previewPhotoFunction;
  final PickPhotoFunction? pickPhotoFunction;
  final PhotoUploadStatusChangedCallback? photoUploadStatusChangedCallback;
  final WidgetBuilder? addWidgetBuilder;

  final bool uploadAutomatically;
  final bool showPlusIcon;
  final Color backgroundColor;

  const PhotoWidget({
    Key? key,
    this.photo,
    this.width,
    this.height,
    this.fit = BoxFit.cover,
    this.placeholder = "ico_no_data.png",
    this.editable = false,
    this.uploadPhotoFunction,
    required this.previewPhotoFunction,
    required this.pickPhotoFunction,
    this.photoUploadStatusChangedCallback,
    this.uploadAutomatically = true,
    this.showPlusIcon = true,
    this.backgroundColor = Colors.white,
    this.addWidgetBuilder,
  }) : super(key: key);

  @override
  PhotoWidgetState createState() => PhotoWidgetState();
}

class PhotoWidgetState extends State<PhotoWidget> {
  FileUploadBean? get photo => _newPhoto ?? widget.photo;

  UploadFileStatus get status => _statusNotifier.value;

  late ValueNotifier<UploadFileStatus> _statusNotifier;
  String? _pickedImagePath;
  FileUploadBean? _newPhoto;

  @override
  void initState() {
    _statusNotifier = ValueNotifier(UploadFileStatus.none);
    if (widget.photoUploadStatusChangedCallback != null) {
      _statusNotifier.addListener(() {
        widget.photoUploadStatusChangedCallback!(photo, _statusNotifier.value);
      });
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final config = Configs.of(context);
    final s = CustomizableFeatures.strings;
    return ValueListenableBuilder(
      valueListenable: _statusNotifier,
      builder: (BuildContext context, UploadFileStatus status, Widget? child) {
        Widget contentWidget;
        switch (status) {
          case UploadFileStatus.uploading:
            contentWidget = SizedBox(
              width: widget.width,
              height: widget.height,
              child: LoadingIndicator(),
            );
            break;
          case UploadFileStatus.failed:
            contentWidget = SizedBox(
              child: _buildFailedWidget(s),
              width: widget.width,
              height: widget.height,
            );
            break;
          default:
            contentWidget = Stack(
              clipBehavior: Clip.none,
              children: <Widget>[
                Center(child: _buildImageLayer(config.imageRoot, s)),
                if (widget.editable)
                  if (photo?.isEmpty ?? true)
                    Visibility(
                      visible: widget.showPlusIcon == true,
                      child: _buildUploadImageLayer(),
                    )
                  else ...[
                    Visibility(
                      visible: widget.uploadAutomatically == true &&
                          photo?.isEmpty == false,
                      child: _buildUpdateButtonLayer(s),
                    ),
                    _buildDeleteButtonLayer(),
                  ]
              ],
            );
        }
        return contentWidget;
      },
    );
  }

  Widget _buildFailedWidget(Strings s) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(s.upload_failed),
        SizedBox(height: 8),
        SizedBox(
          height: 28,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(padding: EdgeInsets.zero),
            child: Text(
              s.retry,
              style: TextStyle(color: Colors.white),
            ),
            onPressed: () {
              _startUpload(_pickedImagePath);
            },
          ),
        ),
      ],
    );
  }

  Widget _buildUpdateButtonLayer(Strings s) {
    return Positioned(
      right: 0,
      left: 0,
      bottom: 0,
      child: SizedBox(
        height: 28,
        child: ElevatedButton(
          child: Text(s.upload_again),
          onPressed: _uploadImage,
          style: ElevatedButton.styleFrom(
            primary: Colors.black.withOpacity(0.6),
            padding: EdgeInsets.zero,
          ),
        ),
      ),
    );
  }

  Widget _buildDeleteButtonLayer() {
    return Positioned(
      right: -16,
      top: -16,
      child: ClearButton(
        onPressed: () {
          photo?.clear();
          _statusNotifier.value = UploadFileStatus.none;
        },
      ),
    );
  }

  Widget _buildImageLayer(String imageRoot, Strings s) {
    if (photo?.isEmpty ?? true) {
      if (widget.showPlusIcon)
        return Image.asset(
          "$imageRoot/${widget.placeholder}",
          fit: BoxFit.cover,
          width: widget.width ?? double.infinity,
          height: widget.height,
        );
      else {
        return InkWell(
          child: widget.addWidgetBuilder != null
              ? widget.addWidgetBuilder!(context)
              : Container(
                  color: widget.backgroundColor,
                  child: Image.asset(
                    "$imageRoot/${widget.placeholder}",
                    width: widget.width ?? double.infinity,
                    height: widget.height,
                  ),
                ),
          onTap: _uploadImage,
        );
      }
    } else if (photo?.hasLocalCache ?? false)
      return InkWell(
        child: Image.file(
          File(photo!.localPath!),
          width: widget.width ?? double.infinity,
          height: widget.height,
          fit: widget.fit,
        ),
        onTap: () {
          widget.previewPhotoFunction?.call(context, photo!);
        },
      );
    else {
      return InkWell(
        child: FadeInImage.assetNetwork(
          placeholder: "$imageRoot/${widget.placeholder}",
          image: photo!.url!,
          fit: widget.fit,
          width: widget.width ?? double.infinity,
          height: widget.height,
        ),
        onTap: () {
          widget.previewPhotoFunction?.call(context, photo!);
        },
      );
    }
  }

  Widget _buildUploadImageLayer() {
    return Positioned.fill(
      child: Center(
        child: SizedBox(
          width: 36,
          height: 36,
          child: ElevatedButton(
            style: ElevatedButton.styleFrom(
              padding: EdgeInsets.symmetric(vertical: 4),
            ),
            child: Icon(
              Icons.add,
              color: Colors.white,
            ),
            onPressed: _uploadImage,
          ),
        ),
      ),
    );
  }

  void _uploadImage() {
    widget.pickPhotoFunction?.call(context).then((imagePath) {
      _pickedImagePath = imagePath;
      photo?.localPath = imagePath;
      _statusNotifier.value = UploadFileStatus.pending;
      if (widget.uploadAutomatically == true &&
          widget.uploadPhotoFunction != null) {
        _startUpload(imagePath);
      }
    });
  }

  void _startUpload(String? imagePath) {
    _statusNotifier.value = UploadFileStatus.uploading;
    widget.uploadPhotoFunction!(imagePath).then((result) {
      if (mounted) {
        if (result.success()) {
          _newPhoto = result.data;
          _newPhoto!.localPath = imagePath;
          _statusNotifier.value = UploadFileStatus.success;
        } else {
          _statusNotifier.value = UploadFileStatus.failed;
        }
      }
    });
  }
}

enum UploadFileStatus { none, pending, uploading, failed, success }

class MultiPhotoWidget extends StatefulWidget {
  final int maxCount;
  final int crossAxisCount;

  final String placeholder;
  final UploadPhotoFunction? uploadPhotoFunction;
  final PreviewPhotoFunction? previewPhotoFunction;
  final PickPhotoFunction? pickPhotoFunction;
  final WidgetBuilder? addWidgetBuilder;

  final bool uploadAutomatically;
  final bool showPlusIcon;
  final Color backgroundColor;

  const MultiPhotoWidget({
    Key? key,
    this.maxCount = 9,
    this.crossAxisCount = 3,
    this.placeholder = "ico_no_data.png",
    this.uploadPhotoFunction,
    this.previewPhotoFunction,
    this.pickPhotoFunction,
    this.uploadAutomatically = true,
    this.showPlusIcon = true,
    this.backgroundColor = Colors.white,
    this.addWidgetBuilder,
  }) : super(key: key);

  @override
  MultiPhotoWidgetState createState() => MultiPhotoWidgetState();
}

class UploadingPhotoEntity {
  FileUploadBean? photo;
  UploadFileStatus? status;

  UploadingPhotoEntity({
    this.photo,
    this.status,
  }) {
    photo ??= FileUploadBean();
    status ??= UploadFileStatus.none;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is UploadingPhotoEntity &&
          runtimeType == other.runtimeType &&
          photo == other.photo;

  @override
  int get hashCode => photo.hashCode;
}

class MultiPhotoWidgetState extends State<MultiPhotoWidget> {
  late List<UploadingPhotoEntity> _uploadingImages;

  @override
  void initState() {
    super.initState();
    _uploadingImages = [UploadingPhotoEntity()];
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: widget.crossAxisCount,
      crossAxisSpacing: 16,
      mainAxisSpacing: 16,
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      padding: EdgeInsets.only(top: 8, right: 8),
      children: <Widget>[
        for (final item in _uploadingImages)
          PhotoWidget(
            key: ValueKey(item.photo),
            photo: item.photo,
            editable: true,
            fit: BoxFit.fill,
            width: double.infinity,
            height: double.infinity,
            placeholder: widget.placeholder,
            showPlusIcon: widget.showPlusIcon,
            backgroundColor: widget.backgroundColor,
            uploadAutomatically: widget.uploadAutomatically,
            pickPhotoFunction: widget.pickPhotoFunction,
            previewPhotoFunction: widget.previewPhotoFunction,
            uploadPhotoFunction: widget.uploadPhotoFunction,
            photoUploadStatusChangedCallback: _onPhotoStatusChanged,
            addWidgetBuilder: widget.addWidgetBuilder,
          )
      ],
    );
  }

  void _onPhotoStatusChanged(FileUploadBean? photo, UploadFileStatus status) {
    print("onPhotoStatusChanged $photo -> $status");
    setState(() {
      final item = UploadingPhotoEntity(photo: photo, status: status);
      final index = _uploadingImages.indexOf(item);
      if (index == -1) {
        if (_uploadingImages.isNotEmpty) {
          _uploadingImages.insert(_uploadingImages.length - 1, item);
        } else {
          _uploadingImages.add(item);
        }
      } else {
        _uploadingImages.removeAt(index);
        if (photo?.isEmpty != true) {
          _uploadingImages.insert(index, item);
        }
      }
      if (_uploadingImages.length > widget.maxCount) {
        _uploadingImages.removeLast();
      } else if (_uploadingImages.length < widget.maxCount) {
        if (!_uploadingImages.last.photo!.isEmpty) {
          _uploadingImages.add(UploadingPhotoEntity());
        }
      }
    });
  }

  bool allUploaded() {
    return !_uploadingImages.any((element) =>
        element.status == UploadFileStatus.failed ||
        element.status == UploadFileStatus.uploading);
  }

  List<FileUploadBean?>? getPhotos() {
    final items = _uploadingImages
        .where((element) {
          if (widget.uploadAutomatically == true)
            return element.status == UploadFileStatus.success;
          else {
            return element.status == UploadFileStatus.pending;
          }
        })
        .map((e) => e.photo)
        .toList();
    return items.isNotEmpty ? items : null;
  }
}
